{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：静态方法，类实例方法和类方法的区别\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "为什么会面试这道题？因为很多人真的搞不清它们的区别，那自然就不知道怎么去调用它们。我在项目中也经常看到一些bug是由于乱用这三种方法而导致的。\n",
    "\n",
    "答案\n",
    "类方法\n",
    "是类对象的方法，在定义时需要在上方使用@classmethod进行装饰，形参为cls，表示类对象，类对象和实例对象都可调用。\n",
    "类实例方法\n",
    "是类实例化对象的方法，只有实例对象可以调用，形参为self，指代对象本身。\n",
    "静态方法\n",
    "是一个任意函数，在其上方使用@staticmethod进行装饰，可以用对象直接调用。\n",
    "拓展\n",
    "答案部分已经很清楚的解释了三种方法的区别所在，那么我们看看具体哪种场景下会使用这些方法。\n",
    "\n",
    "我们先说说静态方法。静态方法是一种比较特殊的方法，比如，我们在一个类中创建一个函数，但是这个函数本身不调用任何类对象，就像下面这个例子一样\n",
    "\n",
    "class Demo(object):\n",
    "    def method(*argv):\n",
    "        return argv\n",
    "\n",
    "    @classmethod\n",
    "    def klassmethod(*argv):\n",
    "        return argv\n",
    "\n",
    "    @staticmethod\n",
    "    def statmethod(*argv):\n",
    "        return argv\n",
    "我们看看这个类中的类实例方法和静态方法的区别：\n",
    "\n",
    ">>> Demo().method\n",
    "<bound method Demo.method of <__main__.Demo object at 0x103bc07d0>>\n",
    ">>> Demo().method()\n",
    "(<__main__.Demo at 0x1041581d0>,)  # <==== 自动把实例对象看做method的第一个参数\n",
    ">>> Demo().statmethod\n",
    "<function __main__.Demo.statmethod(*argv)>\n",
    ">>> Demo().statmethod()\n",
    "()                                 # <==== 没有附加任何参数\n",
    "Tips : 我们定义classmethod的时候，往往参数形式像这样klassmethod(cls, *others)，也就是将参数解包了，因为我们知道，类方法的第一个参数就是类。类实例方法也是如此，因为我们知道它的第一个参数是self对象本身，所以往往我们会这么定义:method(self, *others)\n",
    "\n",
    "method是一个 Demo 实例的绑定方法（bound method），那么怎么理解这个绑定方法呢？当函数是类的属性并且通过类的实例访问它时，Python 将创建称为绑定方法的方法。也就是说，它的必要条件是：一，类实例访问该方法；二，该方法是类属性。也就是说，类实例方法会自动绑定第一个参数，而那个参数就类实例本身。那么，Demo.method呢？\n",
    "\n",
    ">>> Demo.method\n",
    "<function __main__.Demo.method(*argv)>\n",
    "我们这样使用就相当于调用一个普通方法\n",
    "\n",
    ">>> a = Demo()\n",
    ">>> a.method()\n",
    "(<__main__.Demo at 0x1026bd750>,)\n",
    ">>> Demo.method(a)\n",
    "(<__main__.Demo at 0x1026bd750>,)\n",
    "那么类方法呢？\n",
    "\n",
    ">>> Demo.klassmethod\n",
    "<bound method Demo.klassmethod of <class '__main__.Demo'>>\n",
    ">>> Demo().klassmethod\n",
    "<bound method Demo.klassmethod of <class '__main__.Demo'>>\n",
    ">>> b = Demo()\n",
    ">>> b.klassmethod()\n",
    "(__main__.Demo,)\n",
    ">>> Demo.klassmethod()\n",
    "(__main__.Demo,)\n",
    "类方法也会自动绑定第一个参数，但这个参数是类本身。一般我们可以使用类方法去放回一个新的类实例：\n",
    "\n",
    "class Demo:\n",
    "    def __init__(*args):\n",
    "        pass\n",
    "\n",
    "    @classmethod\n",
    "    def create(cls, *args):\n",
    "        # 这里可以对参数进行过滤或验证\n",
    "        # 这里省略掉\n",
    "        return cls(*args) # 验证参数成功后，返回类实例\n",
    "经过以上这些事例代码，我们就很清楚的知道了如何去调用这三个方法。\n",
    "\n",
    "小结\n",
    "@property装饰器定义只读属性\n",
    "类方法，类实例方法和静态方法的区别"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
